﻿//Nishanth

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
using System.Xml.Linq;

namespace Tzync
{
    /***************************************************************************************************/
    /* This Class acts as an interface between the metadata files and the Tree of Nodes in the program */
    /***************************************************************************************************/

    public class MetaData
    {        
        int logFileCreateCounter,depthFirstReadCounter ,nodeIndex, flag;
        private String rootFolder;
        private String logFilePath = Directory.GetCurrentDirectory();
        private List<TreeNode> arrNodes = new List<TreeNode>();
        private List<long> DirectorySize = new List<long>();
        private SystemType systemName;
        private Profile profile;

        /// <summary>
        /// Constructor loads the address of the folder selected,sets the flag and sets the log file counter
        /// </summary>
        /// <param name="p"></param>
        public MetaData(Profile p)
        {            
            profile = p;            
            systemName = p.SystemName;
            if (systemName == SystemType.Host)
                rootFolder = profile.RootAddressHost;
            else
                rootFolder = profile.RootAddressGuest;

            flag = 0;            
            logFileCreateCounter = 0;
        }

        public void XML_DepthFirstReader_wrapper(String metaDataPath)
        {
            
                XElement loaded = XElement.Load(@metaDataPath);
                loaded = loaded.Element("Folder");
                depthFirstReadCounter = 0;
                XML_DepthFirstRead(loaded, 0);
        }

        /// <summary>
        /// Takes in a subtree and the parent index and 
        /// adds the files/folders to the arrnodes
        /// Written By : N Nagappan
        /// </summary>
        /// <param name="D">Subtree to be read</param>
        /// <param name="parent">parent index of the current subtree</param>
        /// <returns>index of the current subtree</returns>
        public int XML_DepthFirstRead(XElement D, int parent)
        {
            #region variables
            int cur = depthFirstReadCounter;
            int countFolder = 0, countFile = 0;
            var folderCheck = D.Element("Folder");
            if (folderCheck != null)
            {
                countFolder = 1;
            }
            var fileCheck = D.Element("File");
            if (fileCheck != null)
            {
                countFile = 1;
            }

            //dummy initialization
            var subDirectories = D.Elements("Folder");
            var subFiles = D.Elements("File");
            if (folderCheck != null)
            {
                var replace = from c in D.Elements("Folder") select c;
                subDirectories = from c in D.Elements("Folder") select c;
            }
            if (fileCheck != null)
            {
                var replace = from c in D.Elements("File") select c;
                subFiles = replace;
            }

            string temp;
            temp = D.Attribute("info").Value.ToString();
            temp = temp.Substring(0, temp.Count() - 1);
            TreeNode n = new TreeNode(temp);
            n.SetId(depthFirstReadCounter);

            arrNodes.Add(n);
            ++depthFirstReadCounter;   
            #endregion variables

            if (countFile > 0)
                foreach (var subFile in subFiles)
                {
                    temp = subFile.Attribute("info").Value.ToString();
                    temp = temp.Substring(0, temp.Count() - 1);
                    n = new TreeNode(temp);
                    n.SetId(depthFirstReadCounter);
                    arrNodes.Add(n);
                    arrNodes[cur].AddChild(depthFirstReadCounter);
                    depthFirstReadCounter++;
                }
            if (countFolder > 0)
                foreach (var subFolder in subDirectories)
                {
                    temp = subFolder.Attribute("info").Value.ToString();
                    temp = temp.Substring(0, temp.Count() - 1);
                    n = new TreeNode(temp);
                    n.SetId(XML_DepthFirstRead(subFolder, cur));
                    arrNodes[cur].AddChild(n.GetId());
                }

            arrNodes[cur].AddChild(-1);
            arrNodes[cur].SetChilds(arrNodes[cur].dynamicChildList);

            return cur;
        }
        //Function to read from the logFile (if it exists) or create one and Create Tree of TreeNodes.
        /// <summary>
        /// Function to load arrNodes from the metaData file
        /// Written by : N Nagappan 
        /// </summary>
        public void LoadMetaData()
        {
            //load from file. create a Node
            String metaDataPath = Path.Combine(profile.ProfilePath, "MetaData.xml");

            if (File.Exists(metaDataPath))
            {
                /******************XML reading starts here**************/
                //loading the xml logFile
                XML_DepthFirstReader_wrapper(metaDataPath);
            }

            else
            {
                //Create a new arrNodes list for the root Folder 
                DFS_wrapper();
                writeToFile();
            }
        }

        /*********************************************************/
        /* Accessory Functions for the data members of the class */
        /*********************************************************/

        public TreeNode getNode(int index)
        {
            if (index < arrNodes.Count)
                return arrNodes[index];
            else return null;
        }

        public void setNode(int index, TreeNode n)
        {
            if (index < arrNodes.Count)
                arrNodes[index] = n;
        }

        public void addNode(TreeNode n)
        {
            arrNodes.Add(n);
        }

        public List<TreeNode> getAllNodes()
        {
            return arrNodes;
        }

        public void DFS_wrapper()
        {
            DirectoryInfo D = new DirectoryInfo(@rootFolder);
            DFS(D, 0);
        }
        /**************************************************************/
        /* Accessory Functions for the data members of the class end !*/
        /**************************************************************/

        //Description     : Function to Create the log File the first time.
        //Pre-Conditions  : Parent is a valid index of arrNode array, Directory D exists.
        //Post-Conditions : A Tree of TreeNode Class is created for the FIRST RUN of the software from system.       

        public int DFS(DirectoryInfo D, int parent)
        {
            int cur = logFileCreateCounter, child;
            DirectoryInfo[] subDirectories = D.GetDirectories();
            FileInfo[] subFiles = D.GetFiles();
            String DString = createEntry(D, parent, subDirectories.Count(), subFiles.Count());
            TreeNode n = new TreeNode(DString), n1;
            arrNodes.Add(n);

            for (int i = 0; i < subFiles.Count(); i++)
            {
                DString = logFileCreateCounter.ToString() + "|"; ;
                //add each File info  
                DString = DString + subFiles[i].FullName + "|" + "|" + "1,0" + "|" + subFiles[i].Length + "|" + "0,0" + "|";
                DString = DString + "0" + "|" + Utility.ParseDateTime(subFiles[i].LastWriteTime) + "|" + Utility.ParseDateTime(subFiles[i].LastWriteTime) + "|";
                DString = DString + cur.ToString() + "|";
                n1 = new TreeNode(DString);
                arrNodes.Add(n1);
                arrNodes[cur].AddChild(logFileCreateCounter);
                logFileCreateCounter++;
            }

            for (int i = 0; i < subDirectories.Count(); i++)
            {
                child = DFS(subDirectories[i], cur);
                arrNodes[cur].AddChild(child);
            }

            arrNodes[cur].AddChild(-1);
            n.SetChilds(n.dynamicChildList);
            return cur;
        }

        //Description     : Function to create a logFile entry for a Directory
        //Pre-Conditions  : Parent is a valid arrNode index, Directory D exists
        //Post-Conditions : A string is created to be given to TreeNode class to create a Tree node from it      

        private String createEntry(DirectoryInfo D, int parent, int f, int d)
        {
            long size = CalculateDirectorySize(D, logFileCreateCounter);
            String DString = logFileCreateCounter.ToString() + "|";
            DString = DString + D.FullName + "|" + "|" + "1,0" + "|" + size.ToString() + "|" + "0,0" + "|";
            DString = DString + "0" + "|" + Utility.ParseDateTime(D.LastWriteTime) + "|" + Utility.ParseDateTime(D.LastWriteTime) + "|" + parent.ToString() + "|" + "-1";
            logFileCreateCounter++;
            return DString;
        }

        //Description     : Function to calculate directory size
        //Pre-Conditions  : Directory D exists
        //Post-Conditions : Size calculated       

        long CalculateDirectorySize(DirectoryInfo directory, int nI)
        {
            if (flag != 0 && DirectorySize[nI] != -1) return DirectorySize[nI];

            int cur = nodeIndex;
            DirectorySize.Add(-1);
            nodeIndex++;
            long totalSize = 0;

            // Examine all contained files.
            FileInfo[] files = directory.GetFiles();
            foreach (FileInfo file in files)
            {
                totalSize += file.Length;
                nodeIndex++;
                DirectorySize.Add(file.Length);
            }

            DirectoryInfo[] dirs = directory.GetDirectories();
            foreach (DirectoryInfo dir in dirs)
            {
                totalSize += CalculateDirectorySize(dir, nodeIndex);
            }

            return DirectorySize[cur] = totalSize;
        }

     
        /// <summary>
        /// Description     : Function to write to metadata.xml using information in arrNodes. 
        ///                   Excludes the entries that are deleted.
        /// Written By      : N Nagappan
        /// Tested  By      : Nishanth
        /// </summary>
        public void writeToFile()
        {
            /********* Writing to XML starts here *********/
            #region variables
            int i = 0, j = 0;
            int[] assignParent = new int[arrNodes.Count];

            XElement srcTree = new XElement("rootFolder");

            int folderCount = 0, fileCount = 0;
            int nodeCount = arrNodes.Count, childCount = 0;
            #endregion

            //inserting the root folder

            srcTree.Add(new XElement("Folder", new XAttribute("FolderIndex", folderCount), new XAttribute("info", arrNodes[i].toString())));
            folderCount++;

           
            for (i = 1; i < nodeCount; ++i)
                if (!arrNodes[i].Isdeleted)
                {
                    #region Add folder element to the XML TREE
                    if (arrNodes[i].GetNodeType() == Type.Folder)
                    {
                        XElement target = srcTree.Descendants("Folder").First(
                              el => (int)el.Attribute("FolderIndex") == assignParent[i]);

                        target.Add(new XElement("Folder", new XAttribute("FolderIndex", folderCount), new XAttribute("info", arrNodes[i].toString())));

                        childCount = arrNodes[i].GetChilds().Count - 1;  //check if -1 is required 
                        for (j = 0; j < childCount; ++j)
                            assignParent[arrNodes[i].GetChilds().ElementAt(j)] = folderCount;

                        folderCount++;
                    } 
                    #endregion

                    #region Add file element to the XML TREE
                    else
                    {
                        XElement target = srcTree.Descendants("Folder").First(
                              el => (int)el.Attribute("FolderIndex") == assignParent[i]);

                        target.Add(new XElement("File", new XAttribute("info", arrNodes[i].toString())));
                        fileCount++;
                    } 
                    #endregion
                }

            srcTree.Save(Path.Combine(profile.ProfilePath, "MetaData.xml"));
            /********* Writing to XML  ends  here *********/
           
        }

    }

    /***********************/
    /* Class MetaData ends */
    /***********************/
}
